package com.example.spiretest.utils;

import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.aspose.cells.License;
import com.aspose.cells.PdfSaveOptions;
import com.aspose.cells.Workbook;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.util.SheetUtil;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
import java.util.stream.Stream;

/**
 * @author: LiuYan
 * @date: 2022/4/21 18:28
 * @description: 注-破解jar在百度网盘中(微信登录)
 */
public class EpiUtil {

    public static String SEPARATOR = "/";
    public static String USER_DIR = System.getProperties().getProperty("user.dir").replace("\\", "/");


    public static String EXCEL_FOM = "xls";
    public static String PDF_FOM = "pdf";
    public static String PNG_FOM = "png";

    public static String EPI_PATH = "spire" + SEPARATOR;
    public static String EXCEL_PATH = EPI_PATH + "excel";
    public static String PDF_PATH = EPI_PATH + "pdf";
    public static String PNG_PATH = EPI_PATH + "png";


    /**
     * 生成excel, 返回生成路径-通过流/字节进行操作
     *
     * @param resultList
     * @return
     */
    public static String HandelExcelByte(Iterable<?> resultList) {
        try {
            // 生成excel - 创建文件前-先删除
            byte[] excelBytes = EpiUtil.createExcel(resultList);

            // excel生成pdf
            byte[] pdfBytes = EpiUtil.excelToPdf(excelBytes);

            // pdf生成img
            byte[] imgBytes = EpiUtil.pdfToImg(pdfBytes);

            System.out.println("excelBytes---" + excelBytes.length + "---pdfBytes---" + pdfBytes.length + "---imgBytes---" + imgBytes.length);

        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return null;
    }

    /**
     * excel -转 png : 生成物理文件进行操作
     *
     * @param resultList
     * @return
     */
    public static String HandelExcelFile(Iterable<?> resultList) {
        try {
            // 生成excel - 创建文件前-先删除
            String xlsPath = EpiUtil.HandelExcel(resultList);

            // 生成pdf&png
            byte[] pdfBytes = EpiUtil.HandelExcelToPng(xlsPath);

        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return null;
    }


    /**
     * 生成excel, 返回生成路径
     *
     * @param resultList
     * @return
     */
    public static String HandelExcel(Iterable<?> resultList) {
        try {
            // 生成excel - 创建文件
            String xlsxPath = randomFileName(EXCEL_PATH, EXCEL_FOM);
            EpiUtil.createExcel(xlsxPath, resultList);
            return xlsxPath;
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return null;
    }

    /**
     * excel->pdf->png, 返回png字节数组
     *
     * @param xlsxPath
     * @return
     */
    public static byte[] HandelExcelToPng(String xlsxPath) {
        try {
            // excel转PDF
            String pdfPath = operatingPlatform(PDF_PATH, "", PDF_FOM);
            EpiUtil.excelToPdf(xlsxPath, pdfPath, null);
            // PDF转png
            String pngPath = operatingPlatform(PNG_PATH, "", PNG_FOM);
            EpiUtil.pdfToImg(pdfPath, pngPath);
            byte[] bytes = EpiUtil.readByNIO(pngPath);
            return bytes;
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        }
    }


    /**
     * 生成excel - 流
     *
     * @param resultList
     * @throws Exception
     */
    public static byte[] createExcel(Iterable<?> resultList) throws Exception {
        // 生成excel
        ExcelWriter writer = new ExcelWriter();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // 设置只导出有别名的字段
            writer.setOnlyAlias(true);
            // 一次性写出内容，使用默认样式，强制输出标题
            writer.write(resultList, true);
            // 修改excel显示样式
            excelFormat(writer);
            // 写入
            writer.flush(out);
            byte[] bytes = out.toByteArray();
            return bytes;
        } catch (Exception e) {
            throw new Exception("执行 createExcel()出错, 错误信息:" + e.getMessage());
        } finally {
            // 关闭writer，释放内存
            writer.close();
            closeOutputStream(out);
        }
    }


    /**
     * 生成excel - 物理文件
     *
     * @param excelPath
     * @param resultList
     * @throws Exception
     */
    public static void createExcel(String excelPath, Iterable<?> resultList) throws Exception {
        // 生成excel
        ExcelWriter writer = new ExcelWriter(excelPath);
        try {
            // 设置只导出有别名的字段
            writer.setOnlyAlias(true);
            // 一次性写出内容，使用默认样式，强制输出标题
            writer.write(resultList, true);
            // 修改excel显示样式
            excelFormat(writer);
        } catch (Exception e) {
            throw new Exception("执行 createExcel()出错, 错误信息:" + e.getMessage());
        } finally {
            // 关闭writer，释放内存
            writer.close();
        }
    }

    /**
     * 修改excel 样式
     *
     * @param writer
     */
    private static void excelFormat(ExcelWriter writer) {
        // 设置表头颜色
        StyleSet style = writer.getStyleSet();
        CellStyle cellStyle = style.getHeadCellStyle();
        cellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
        // 设置内容宽度
        int columnCount = writer.getColumnCount();
        for (int i = 0; i < columnCount; ++i) {
            double width = SheetUtil.getColumnWidth(writer.getSheet(), i, false);
            if (width != -1.0D) {
                width *= 270.0D;
                // 此处可以适当调整，调整列空白处宽度
                width += 220D;
                writer.setColumnWidth(i, Math.toIntExact(Math.round(width / 256D)));
            }
        }
    }


    /**
     * excel转PDF - 流
     *
     * @throws Exception
     */
    public static byte[] excelToPng(byte[] xlsByte) throws Exception {
        // 读取
        InputStream ins = null;
        // 转换后
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // 验证 License
            getLicense();
            // 读取excel字节数组
            ins = new ByteArrayInputStream(xlsByte);
            // 生成pdf
            Workbook wb = new Workbook(ins);
            PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
            pdfSaveOptions.setOnePagePerSheet(true);
            wb.save(out, pdfSaveOptions);
            // pdf 转png
            byte[] bytes = pdfToImg(out.toByteArray());
            return bytes;
        } catch (Exception e) {
            throw new Exception("执行 excelToPdf()出错, 错误信息:" + e.getMessage());
        } finally {
            closeInputStream(ins);
            closeOutputStream(out);
        }
    }


    /**
     * excel转PDF - 流
     *
     * @throws Exception
     */
    public static byte[] excelToPdf(byte[] xlsByte) throws Exception {
        // 读取
        InputStream ins = null;
        // 转换后
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // 验证 License
            getLicense();
            // 读取excel字节数组
            ins = new ByteArrayInputStream(xlsByte);
            // 生成pdf
            Workbook wb = new Workbook(ins);
            PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
            pdfSaveOptions.setOnePagePerSheet(true);
            wb.save(out, pdfSaveOptions);
            byte[] bytes = out.toByteArray();
            return bytes;
        } catch (Exception e) {
            throw new Exception("执行 excelToPdf()出错, 错误信息:" + e.getMessage());
        } finally {
            closeInputStream(ins);
            closeOutputStream(out);
        }
    }

    /**
     * excel转PDF
     *
     * @param excelFilePath
     * @param pdfFilePath
     * @param convertSheets
     * @throws Exception
     */
    public static void excelToPdf(String excelFilePath, String pdfFilePath, int[] convertSheets) throws Exception {
        // 读取
        FileOutputStream fileOS = new FileOutputStream(pdfFilePath);
        try {
            // 验证 License
            getLicense();
            // 生成pdf
            Workbook wb = new Workbook(excelFilePath);
            PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
            pdfSaveOptions.setOnePagePerSheet(true);
            if (null != convertSheets) {
                printSheetPage(wb, convertSheets);
            }
            wb.save(fileOS, pdfSaveOptions);
            fileOS.flush();
        } catch (Exception e) {
            throw new Exception("执行 excelToPdf()出错, 错误信息:" + e.getMessage());
        } finally {
            fileOS.close();
        }
    }


    /**
     * excel转PDF - 隐藏workbook中不需要的sheet页。
     *
     * @param sheets 显示页的sheet数组
     */
    private static void printSheetPage(Workbook wb, int[] sheets) {
        for (int i = 1; i < wb.getWorksheets().getCount(); i++) {
            wb.getWorksheets().get(i).setVisible(false);
        }
        if (null == sheets || sheets.length == 0) {
            wb.getWorksheets().get(0).setVisible(true);
        } else {
            for (int i = 0; i < sheets.length; i++) {
                wb.getWorksheets().get(i).setVisible(true);
            }
        }
    }


    /**
     * 获取 license 去除水印, 若不验证则:
     * 1.转化出的pdf文档会有水印产生;
     * 2.服务器生成pdf存在上限
     */
    private static void getLicense() {
        String licenseXml = "<License>\n" +
                "  <Data>\n" +
                "    <Products>\n" +
                "      <Product>Aspose.Total for Java</Product>\n" +
                "    </Products>\n" +
                "    <EditionType>Enterprise</EditionType>\n" +
                "    <SubscriptionExpiry>20991231</SubscriptionExpiry>\n" +
                "    <LicenseExpiry>20991231</LicenseExpiry>\n" +
                "    <SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>\n" +
                "  </Data>\n" +
                "  <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>\n" +
                "</License>";
        try {
            InputStream is = new ByteArrayInputStream(licenseXml.getBytes(StandardCharsets.UTF_8));
            License license = new License();
            license.setLicense(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * pdf转PNG - 流
     *
     * @param pdfByte
     * @return
     * @throws Exception
     */
    public static byte[] pdfToImg(byte[] pdfByte) throws Exception {
        PDDocument doc = null;
        // 转换后
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // 验证 License
            getLicense();
            doc = PDDocument.load(pdfByte);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for (int i = 0; i < pageCount; i++) {
                BufferedImage image = renderer.renderImageWithDPI(i, 296);
                renderer.renderImage(i, 2.5f);
                ImageIO.write(image, "PNG", out);
            }
            return out.toByteArray();
        } catch (Exception e) {
            throw new Exception("执行 pdfToImg()出错, 错误信息:" + e.getMessage());
        } finally {
            // 关闭流
            doc.close();
            closeOutputStream(out);
        }
    }


    /**
     * pdf转PNG
     *
     * @param pdfPath
     * @param ImagePath
     * @throws Exception
     */
    public static void pdfToImg(String pdfPath, String ImagePath) throws Exception {
        File file = new File(pdfPath);
        PDDocument doc = null;
        try {
            // 验证 License
            getLicense();
            doc = PDDocument.load(file);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for (int i = 0; i < pageCount; i++) {
                BufferedImage image = renderer.renderImageWithDPI(i, 296);
                renderer.renderImage(i, 2.5f);
                ImageIO.write(image, "PNG", new File(ImagePath));
            }
        } catch (Exception e) {
            throw new Exception("执行 pdfToImg()出错, 错误信息:" + e.getMessage());
        } finally {
            // 关闭流
            doc.close();
        }
    }


    public static byte[] readByNIO(String filePath) throws Exception {
        File file = new File(filePath);
        checkFileExists(file);
        FileChannel fileChannel = null;
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            fileChannel = in.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate((int) fileChannel.size());

            while (fileChannel.read(buffer) > 0) {
            }
            return buffer.array();
        } finally {
            closeChannel(fileChannel);
            closeInputStream(in);
        }
    }

    private static void checkFileExists(File file) throws Exception {
        if (file == null || !file.exists()) {
            System.err.println("file is not null or exist !");
            throw new Exception(file.getName());
        }
    }

    private static void closeChannel(FileChannel channel) {
        try {
            if (channel.isOpen()) {
                channel.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void closeOutputStream(OutputStream bos) {
        try {
            if (null != bos) {
                bos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void closeInputStream(InputStream in) {
        try {
            if (null != in) {
                in.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 创建文件
     */
    public static String operatingPlatform(String xPath, String fileName, String fileType) throws Exception {
        try {
            String userDir = USER_DIR + SEPARATOR;
            if (StringUtils.isNotEmpty(xPath)) {
                userDir = userDir + xPath + SEPARATOR;
            }
            File file = new File(userDir);
            if (!file.exists()) {
                // 如果文件夹不存在,则创建(注意:如果是Linux这一步可能出错,可能没有文件夹创建权限,注意)
                file.mkdirs();
            }
            // 标示
            boolean flag = false;
            StringBuffer sbf = new StringBuffer();
            if (StringUtils.isEmpty(fileName) && StringUtils.isNotEmpty(fileType)) {
                String yyyyMMdd = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
                userDir = sbf.append(userDir).append(yyyyMMdd).append(System.nanoTime()).append(".").append(fileType).toString();
                flag = true;
            } else if (StringUtils.isNotEmpty(fileName)) {
                int lastIndexOf = fileName.lastIndexOf(".");
                if (lastIndexOf > 0) {
                    userDir = userDir + fileName;
                    flag = true;
                } else {
                    if (StringUtils.isNotEmpty(fileType)) {
                        userDir = sbf.append(userDir).append(fileName).append(".").append(fileType).toString();
                        flag = true;
                    }
                }
            }
            // 创建文件
            if (flag) {
                File file1 = new File(userDir);
                if (!file1.exists()) {
                    file1.createNewFile();
                }
            }
            return userDir;
        } catch (Exception e) {
            throw new Exception("执行 operatingPlatform()出错, 错误信息:" + e.getMessage());
        }
    }

    /**
     * 生成文件名
     */
    public static String randomFileName(String xPath, String fileType) {
        String userDir = USER_DIR + SEPARATOR;
        if (StringUtils.isNotEmpty(xPath)) {
            userDir = userDir + xPath + SEPARATOR;
        }
        String yyyyMMdd = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        userDir = new StringBuffer().append(userDir).append(yyyyMMdd).append(System.nanoTime()).append(".").append(fileType).toString();
        return userDir;
    }

    /**
     * 删除文件 - 即使未成功删除, 也不能影响下一步执行
     */
    public static void operatingPlatformDelete(String xPath) {
        String userDir = USER_DIR + SEPARATOR;
        if (StringUtils.isNotEmpty(xPath)) {
            userDir = userDir + xPath;
        }
        File file = new File(xPath);
        if (file == null || !file.exists()) {
            return;
        }
        Path path = Paths.get(userDir);
        try (Stream<Path> walk = Files.walk(path)) {
            walk.sorted(Comparator.reverseOrder())
                    .forEach(e -> {
                        try {
                            Files.delete(e);
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                    });
        } catch (Exception e) {
            System.err.printf("无法删除的路径:", path, e);
        }
    }


}
